/*******************************************************************************
 * Witz - Winboard Chess Engine                                                *
 * HASH.C                                                                      *
 *                                                                             *
 * Countains anything that requires hashing, like transposition tables         *
 *                                                                             *
 * Please refer to the licence notice in MAIN.C                                *
 *                                                                             *
 * Copywright (C) 2006 Pradu Kannan                                            *
 * http://witz.sf.net/                                                         *
 ******************************************************************************/
#include <windows.h>

#include "build.h"
#include "defs.h"
#include "protos.h"
#include "data.h"

#include "threads.h"

TTentry *TT; //the transposition table

/*resetTT()
 *resets the trasposition table
 */
void resetTT()
{
	U32 i;
	TTentry nullTTentry={0,0,U64EMPTY,0};
	if(TT==NULL) return;
	for(i=0;i<TT_SIZE;i++)
		TT[i]=nullTTentry;
}

/*recordTT()
 *records a position into the transposition table
 *@param pos - the board position
 *@param m - best move for the board position
 *@param score - the score for this move
 *@param flag - the type of TT entry (ALPHA, BETA, EXACT)
 *@param d - depth left in the search
 *@param dfr - depth from root
 */
void recordTT(const board* pos, move m, int score, char flag, short d, short dfr)
{
	//if called from a verified nullmove search, don't try to record to the TT
	/*if(extractSFNoVerify(pos->SF))
		return;*/

	TTentry* hashentry = TT+(pos->hashkey%TT_SIZE);
	
	if(!threaded)
		EnterCriticalSection(&TTLock);

	//replace if currentdepth+currentage >= TTdepth+TTage
	if(gamedepth+initialMoveNumber+(d<0?0:d) >= hashentry->age + extractDepth(hashentry->data))
	{

		hashentry->age=gamedepth+initialMoveNumber;
		hashentry->key=pos->hashkey;
		//Mate scores are bounded to make sure that we didn't miss a shorter mate
		//in the next turn. They are given the value mate in 1000 to make cuttoffs
		//when necessary and require a search to make sure score is a mate.
		if(score<=-MATE+1000) //negative mate
		{
			flag=ALPHA; //Make -mate scores as upper bounds
			hashentry->score=-MATE+1000; //Score = mate in 1000 plys
		}
		else if(score>=MATE-1000) //positive mate
		{
			flag=BETA; //Make +mate scores as lower bounds
			hashentry->score=MATE-1000; //Score = mate in 1000 plys
		}
		else
			hashentry->score=score;
		hashentry->data=encodeMove(m)+encodeDepth((d<0?0:d))+encodeFlag(flag);

	}

	if(!threaded)
		LeaveCriticalSection(&TTLock);
}

/*probeTT()
 *extracts information stored in the transposition table about a move
 *@param pos - the board position
 *@param tt_probe - a pointer to a tt_probe variable to be updated
 *@returns true if the probe was a hit
 */
bool probeTT(const board* pos, TTprobe* tt_probe)
{
	TTentry* hashentry = TT+(pos->hashkey%TT_SIZE);

	if(hashentry->key != pos->hashkey)
		return false;

	if(!threaded)
		EnterCriticalSection(&TTLock);

	tt_probe->m=extractMove(hashentry->data);
	tt_probe->score=hashentry->score;
	tt_probe->depth=extractDepth(hashentry->data);
	tt_probe->flag=extractFlag(hashentry->data);

	if(!threaded)
		LeaveCriticalSection(&TTLock);

	return true;
}

/*dumpState()
 *dumps the engine state into a file which is useful for debugging
 *the name of the file:
 *[plyNumber_moveJustPlayed_sizeOfHashTable]
 *EG: ply0, ply1, ply2
 */
void dumpState()
{
	char filename[1024];
	FILE* dumpFile;

	sprintf(filename,"%d_%d",gamedepth,TT_SIZE);
	dumpFile=fopen(filename,"wb");

	if(dumpFile==NULL)
		printf("Could not dump hash into %s\n",filename);

	//first element in the dump file is the board position
	fwrite(&mainboard,sizeof(mainboard),(size_t)1,dumpFile);

	//then the game history
	fwrite(gameHistory,sizeof(board),(size_t)MAX_GAMELENGTH,dumpFile);

	//then the game depth
	fwrite(&gamedepth,sizeof(gamedepth),(size_t)1,dumpFile);

	//then the transposition table
	fwrite(TT,sizeof(TTentry),(size_t)TT_SIZE,dumpFile);
}

/*loadState()
 *loads a state as done by the dump file
 *NOTE: the configuration of the exe must be exactly identical for this to work
 *
 *@param filename - the engine state dump to load
 */
void loadState(char* filename)
{
	FILE* dumpFile;
	dumpFile=fopen(filename,"rb");
	if(dumpFile==NULL)
	{
		printf("Unable to open dump file %s\n",dumpFile);
		return;
	}

	//Make sure the computer dosen't start making a move after loading
	compside=NOSIDE;

	//first element in the dump file is the board position
	fread(&mainboard,sizeof(mainboard),(size_t)1,dumpFile);

	//then the game history
	fread(gameHistory,sizeof(board),(size_t)MAX_GAMELENGTH,dumpFile);

	//then the game depth
	fread(&gamedepth,sizeof(gamedepth),(size_t)1,dumpFile);

	//then the transposition table
	fread(TT,sizeof(TTentry),(size_t)TT_SIZE,dumpFile);

	printf("Loaded %s\n\n",filename);
	generatePieceSquare(&mainboard);
	generateLegalMoves(&mainboard,&mainMoveList);
}
